package com.closer.codebox.biz.java;


import com.closer.codebox.biz.CommonTemplate;
import com.closer.codebox.biz.MemoryMonitorService;
import com.closer.codebox.support.dto.ExecuteCodeRequest;
import com.closer.codebox.support.dto.JudgeCase;
import com.closer.codebox.support.dto.JudgeConfig;
import com.closer.codebox.support.dto.JudgeInfo;
import com.closer.codebox.support.enums.JudgeInfoMessageEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class JavaCodeSandbox extends CommonTemplate {

    private final MemoryMonitorService memoryMonitorService;

    public JavaCodeSandbox(MemoryMonitorService memoryMonitorService) {
        this.memoryMonitorService = memoryMonitorService;
    }


    public JudgeInfo executeCode(ExecuteCodeRequest  params) {

        var code = params.getCode();
        //1.保存代码文件
        var userCodeFile = saveCodeToFile(code, "Main.java","java");
        //2.编译代码
        var compileResult = compileFile(userCodeFile);
        if(compileResult != null){
            cleanTempFiles(userCodeFile);
            return JudgeInfo.builder()
                    .message(JudgeInfoMessageEnum.COMPILE_ERROR.getValue())
                    .memory(null)
                    .time(null)
                    .build();
        }
        //3.运行代码
        var result = runFile(userCodeFile, params.getTestCases(),params.getJudgeConfig());

        cleanTempFiles(userCodeFile);
        return JudgeInfo.builder()
                .message(result.getMessage())
                .memory(result.getMemory())
                .time(result.getTime())
                .build();
    }

    /**
     * 编译文件
     * @param file 文件名
     * @return 错误信息
     */
    public String compileFile(File file) {
        var compileCmd = String.format("javac -encoding utf-8 %s", file.getAbsolutePath());

        try {
            var processBuilder = new ProcessBuilder();
            var isWindows = System.getProperty("os.name").toLowerCase().contains("windows");
            processBuilder.command(isWindows ? "cmd.exe" : "sh", isWindows ? "/c" : "-c", compileCmd);
            processBuilder.directory(file.getParentFile());
            processBuilder.redirectErrorStream(true);

            var process = processBuilder.start();

            // 读取输出
            var reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            var output = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                output.append(line).append("\n");
            }

            // 等待进程完成
            var finished = process.waitFor(10, TimeUnit.SECONDS);
            if (!finished) {
                process.destroyForcibly();
                return "编译超时：超过10秒未完成";
            }

            if (process.exitValue() == 0) {
                return null; // 编译成功
            } else {
                var errorMessage = output.toString();
                var simplifiedError = errorMessage.replace(file.getAbsolutePath(), file.getName());
                return "编译错误：" + simplifiedError;
            }
        } catch (Exception e) {
            return "编译异常：" + e.getMessage();
        }
    }

    /**
     * 运行文件
     * @param file 文件名
     * @param testCases 测试用例
     * @return 错误信息
     */
    public JudgeInfo runFile(File file, List<JudgeCase> testCases, JudgeConfig judgeConfig) {

        var timeLimit = judgeConfig.getTimeLimit();
        var maxTime = 0L;
        var maxMemory = 0L;

        // 获取类名和类路径
        var className = file.getName().replace(".java", "");
        var classPath = file.getParentFile();
        for (var testCase : testCases){
            var input = testCase.getInput();
            var output = testCase.getOutput();
            Process process = null;

            try {
                var startTime = System.currentTimeMillis();
                var command = new ArrayList<String>();
                command.add("java");
                command.add("-cp");
                command.add(classPath.getAbsolutePath());

                command.add("-Xmx256m");
                command.add("-Xms64m");
                command.add(className);

                var processBuilder = new ProcessBuilder(command);
                processBuilder.directory(classPath);
                processBuilder.redirectErrorStream(true);

                process = processBuilder.start();

                final long finalPid = process.pid();
                var memoryFuture = CompletableFuture.supplyAsync(() ->
                        memoryMonitorService.monitorPeakMemory(finalPid, timeLimit + 1000)
                );

                // 写入输入
                if (input != null && !input.isEmpty()) {
                    try (var outputStream = process.getOutputStream();
                         var writer = new OutputStreamWriter(outputStream)) {
                        writer.write(input);
                        writer.flush();
                    }
                }

                // 读取输出
                var outputBuilder = new StringBuilder();
                try (var inputStream = process.getInputStream();
                     var reader = new BufferedReader(new InputStreamReader(inputStream))) {
                    String line;
                    while ((line = reader.readLine()) != null) {
                        outputBuilder.append(line).append("\n");
                    }
                }

                // 等待进程完成并获取执行时间
                var finished = process.waitFor(timeLimit, TimeUnit.MILLISECONDS);
                var endTime = System.currentTimeMillis();
                var executionTime = endTime - startTime;

                var peakMemoryUsage = memoryFuture.get(1, TimeUnit.SECONDS);

                if (!finished) {
                    process.destroyForcibly();
                    return JudgeInfo.builder()
                            .message(JudgeInfoMessageEnum.TIME_LIMIT_EXCEEDED.getValue())
                            .time(maxTime)
                            .memory(maxMemory)
                            .build();
                }

                // 检查退出代码
                int exitCode = process.exitValue();
                if (exitCode != 0) {
                    return JudgeInfo.builder()
                            .message(JudgeInfoMessageEnum.RUNTIME_ERROR.getValue())
                            .time(null)
                            .memory(null)
                            .build();
                }

                // 更新最大时间和内存
                maxTime = Math.max(maxTime, executionTime);
                maxMemory = Math.max(maxMemory, peakMemoryUsage);
                if (maxMemory > judgeConfig.getMemoryLimit()*1024) {
                    return JudgeInfo.builder()
                            .message(JudgeInfoMessageEnum.MEMORY_LIMIT_EXCEEDED.getValue())
                            .time(null)
                            .memory(null)
                            .build();
                }
                var realOutput = outputBuilder.toString().trim();
                log.info("测试用例 - 输入: {}, 预期输出: {}, 实际输出: {}, 内存: {}KB, 时间: {}ms",
                        input, output, realOutput, peakMemoryUsage, executionTime);
                if (!realOutput.equals(output)){
                    return JudgeInfo.builder()
                            .message(JudgeInfoMessageEnum.WRONG_ANSWER.getValue())
                            .time(maxTime)
                            .memory(maxMemory)
                            .build();
                }
            } catch (Exception e) {
                if (process != null && process.isAlive()) {
                    process.destroyForcibly();
                }
                log.error("执行测试用例时发生错误: {}", e.getMessage());
                return JudgeInfo.builder()
                        .message(JudgeInfoMessageEnum.RUNTIME_ERROR.getValue())
                        .time(null)
                        .memory(null)
                        .build();
            }
        }
        return JudgeInfo.builder()
                .message(JudgeInfoMessageEnum.ACCEPTED.getValue())
                .time(maxTime)
                .memory(maxMemory)
                .build();
    }

}
